﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cloo;
using Processing.Exceptions;

namespace Processing
{
    /// <summary>
    /// Modelliert eine spezifische Recheneinheit, bzw. einen Recheneinheitenverbund.
    /// </summary>
    public class Processor : IDisposable
    {
        #region Attribute
        private ComputeDevice device;
        private ComputePlatform platform;
        private ComputeContextPropertyList properties;
        private ComputeContext context;
        private bool debugMode;
        #endregion Attribute

        #region Eigenschaften
        /// <summary>
        /// Gibt das verwendete Gerät an.
        /// </summary>
        public ComputeDevice Device { get { return device; } }

        /// <summary>
        /// Gibt die verwendete Plattform an.
        /// </summary>
        public ComputePlatform Platform { get { return platform; } }

        /// <summary>
        /// [INTERN] gibt den Kontext an.
        /// </summary>
        internal ComputeContext Context { get { return context; } }

        /// <summary>
        /// Gibt und setzt den Indikator, ob der Debugmodus aktiviert ist.
        /// (Insbesondere für ATi-Karten mit Treiberproblemen wichig!)
        /// </summary>
        public bool DebugMode { get { return debugMode; } set { debugMode = value; } }
        #endregion Eigenschaften

        #region Methoden
        #region Konstruktoren und Destruktoren
        /// <summary>
        /// Instanziiert und initialisiert die Klasse.
        /// </summary>
        /// <param name="platform">Die Platform</param>
        /// <param name="device">Das Gerät.</param>
        public Processor(ComputePlatform platform, ComputeDevice device)
        {
            if (platform == null) throw new ArgumentNullException("platform");
            if (device == null) throw new ArgumentNullException("device");
            this.platform = platform;
            this.device = device;
            properties = new ComputeContextPropertyList(platform);
            context = new ComputeContext(new ComputeDevice[] { device }, properties, null, IntPtr.Zero);
        }
        #endregion Konstruktoren und Destruktoren

        /// <summary>
        /// Allokiert einen Buffer und überträgt in diesen die angegebenen Daten.
        /// </summary>
        /// <typeparam name="T">Der Typ.</typeparam>
        /// <param name="data">Die Daten.</param>
        /// <param name="readOnly">Indikator ob es sich um einen ReadOnly Buffer handelt. Wenn ja wird kein Lesen von Daten von der GPU möglich sein, folglich für Debugzwecke geeignet.</param>
        /// <returns>Der Buffer.</returns>
        public Buffer<T> AllocateBuffer<T>(T[] data, bool readOnly) where T : struct
        {
            try { return new Buffer<T>(this, data, readOnly); }
            catch (Exception e) { throw new ProcessorException("Der Buffer konnte nicht allokiert werden.", e); }
        }

        /// <summary>
        /// Allokiert einen Buffer und überträgt in diesen die angegebenen Daten.
        /// </summary>
        /// <typeparam name="T">Der Typ.</typeparam>
        /// <param name="data">Die Daten.</param>
        /// <returns>Der Buffer.</returns>
        public Buffer<T> AllocateBuffer<T>(T[] data) where T : struct
        {
            try { return new Buffer<T>(this, data); }
            catch (Exception e) { throw new ProcessorException("Der Buffer konnte nicht allokiert werden.", e); }
        }

        /// <summary>
        /// Allokiert einen Buffer der gegebenen Länge.
        /// </summary>
        /// <typeparam name="T">Der Typ.</typeparam>
        /// <param name="length">Die Länge.</param>
        /// <returns>Der Buffer.</returns>
        public Buffer<T> AllocateBuffer<T>(long length) where T : struct
        {
            try { return new Buffer<T>(this, length); }
            catch (Exception e) { throw new ProcessorException("Der Buffer konnte nicht allokiert werden.", e); }
        }

        /// <summary>
        /// Überlässt das Objekt dem Garbage-Collector.
        /// </summary>
        public void Dispose()
        {
            context.Dispose();
            GC.Collect();   // Wichtig, da sonst der Kontext ggf. bestehen bleibt, und Ressourcen solange belegt bleiben.
        }
        #endregion Methoden
    }
}
